/*
 * Copyright 2010 Marcin Blazejewski
 *
 * This file is part of Nofate.
 *
 * Nofate is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Nofate is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Nofate. If not, see <http://www.gnu.org/licenses/>.
 *
 */


#include "ui.hpp"
#include <abstract/action.hpp>
#include <abstract/attribute.hpp>
#include <iostream>

static MM mm;
static Textbox tb(0, 0, 0, 0);
static char tmp[2] = " ";

Action    *mm_process_input(AreaMap *a);
int        scroll(int dx, int dy);
void       pickup(Creature *cre);

#define TB_H                6

int mm_init(int w, int h) {
    memset(&mm, 0, sizeof(mm));
    memset(&tb, 0, sizeof(Textbox));
    mm.vw = w;
    mm.vh = h - TB_H;
    mm.stay_alive = 1;

    tb.top = mm.vh;
    tb.width = w;
    tb.height = TB_H;
    tb.fg = 0xC0C0C0;
    

    if ( re_init(w, h) != 0 ) throw "RE init";

    return 0;
}

void mm_end() {
    re_end();
}

void mm_bind(Creature *cre, AreaMap *area) {
    mm.cre = cre;
    mm.area = area;
    tmp[0] = cre->ch;
}

int mm_tick() {
    Creature *player = mm.cre;
    Action *a;

    if (player->busy == 0) {

        mm.area->relight();

        fov_map(player->mv, player->x, player->y, 50);
            
        /* auto scroll view when player walks close to the edge */
        /* TODO: replace 6 and 12 with % of vw,vh */
        if ((player->x - mm.vox) < 6) scroll(-12, 0);
        if ((player->x - mm.vox) > (mm.vw - 6)) scroll(12, 0);
        if ((player->y - mm.voy) < 6) scroll(0, -12);
        if ((player->y - mm.voy) > (mm.vh - 6)) scroll(0, 12);

        while ( mm.stay_alive && (player->busy == 0) ) {
            if (mm.dialog) {
                // DRAW
                mm.dialog->draw();
                tb.draw();
                re_flush();

                // INPUT
                a = mm.dialog->process_input(mm.area);

                // DECIDE
                switch (mm.dialog->ret_code) {
                case DRC_CLOSE_ALL:
                    while (mm.dialog) {
                        Dialog *parent = mm.dialog->parent;
                        delete mm.dialog;
                        mm.dialog = parent;
                    }
                    break;
                case DRC_BACK:
                    Dialog *parent = mm.dialog->parent;
                    delete mm.dialog;
                    mm.dialog = parent;
                    break;
                }
            } else { /* no dialog */

                // DRAW
                re_draw_view(player->mv, mm.vox, mm.voy, mm.vw, mm.vh);
                re_puts(player->x - mm.vox, player->y - mm.voy, 0xA0A0A0, 0x0, tmp);
                tb.draw();
                re_flush();

                // INPUT
                a = mm_process_input(mm.area);
            }

            // add any action from input processing above
            if (a != NULL) {
                if (mm.area->add_action(a, player) != 0) continue;
            }
        } /* while not busy */
    } 

    return mm.stay_alive;
}

void mm_message(const char *txt, char is_temp) {
    tb.message(txt, is_temp);
}

void debug() {
    re_flush();
}

void inventory() {
    int i, n = 0;
    std::vector<Thing*>::iterator r;
    re_flush();
    std::list<Thing*>::iterator ii;

    for (r = mm.cre->containers.begin(); r != mm.cre->containers.end(); r++) {
        Thing *con_t = (Thing*)*r;
        Acontainer *con = (Acontainer*)con_t->attributes["container"];
        n = con->items.size();
    }

    Dialog *d = new Dialog();

    n = 0;
    for (r = mm.cre->containers.begin(); r != mm.cre->containers.end(); r++) {
        Thing *con_t = (Thing*)*r;
        Acontainer *con = (Acontainer*)con_t->attributes["container"];

        for (ii = con->items.begin(); ii != con->items.end(); ii++) {
            ListItem li;

            li.txt = (*ii)->name.c_str();
            li.flags = DF_FOCUSABLE;
            li.obj_type = 0;
            li.obj = *ii;

            d->items.push_back(li);
        }
    }

    mm.dialog = d;
}

Action *mm_process_input(AreaMap *area) {
    SDL_Event e;
    Action *a = NULL;
    int redraw = 0;

    while (a == NULL) {
        /* error */
        if (SDL_WaitEvent(&e) == 0) {
            cerr << "[ERR] process events: " << SDL_GetError() << endl;
            mm.stay_alive = 0;
            return NULL;
        }
        /* quit */
        else if (e.type == SDL_QUIT) {
            mm.stay_alive = 0;
            return NULL;
        }
        /* keyboard */
        else if (e.type == SDL_KEYUP) {
            char ctrl = e.key.keysym.mod & KMOD_CTRL;
            char shift = e.key.keysym.mod & KMOD_SHIFT;
            redraw = 0;
            
            if (!shift && !ctrl) {
                switch (e.key.keysym.sym) {
                case SDLK_q: 
                    mm.stay_alive = 0;
                    return NULL;
                case SDLK_RIGHT: a = new MoveAction( 1,  0); break;
                case SDLK_LEFT:  a = new MoveAction(-1,  0); break;
                case SDLK_UP:    a = new MoveAction( 0, -1); break;
                case SDLK_DOWN:  a = new MoveAction( 0,  1); break;
                case SDLK_p: pickup(mm.cre); return NULL;
                case SDLK_i: inventory(); return NULL;
                case SDLK_d: debug(); break;
                default: break;
                }
            } else if (!shift && ctrl) {             
                switch (e.key.keysym.sym) {
                case SDLK_RIGHT: redraw = scroll( 2, 0); break;
                case SDLK_LEFT:  redraw = scroll(-2, 0); break;
                case SDLK_UP:    redraw = scroll(0, -2); break;
                case SDLK_DOWN:  redraw = scroll(0,  2); break;
                default: break;
                }
               
                if (redraw) break;
            }
            break;
        } /* keyup */
    } /* while a==NULL */

    return a;
}

void pickup(Creature *cre) {
    list<Thing*>::iterator i;
    Thing *itm;
    Acontainer *con = NULL;

    for (i=cre->area->things.begin(); i != cre->area->things.end(); i++) {
        itm = *i;
        if ((itm->type == TH_TYPE_THING) && (itm->x == cre->x) && (itm->y == cre->y)) {
            if ((con = con_find_target(cre, itm)) != NULL) {
                con->pickup( itm, cre);
            }
            
            break;
        }
    }
}

int scroll(int dx, int dy) {
    mm.vox += dx;

    /* TODO: replace 100 with area width */
    if ((mm.vox < 0) || (mm.vox >= (100-mm.vw))) {
        mm.vox -= dx;
        dx = 0;
    }

    mm.voy += dy;

    /* TODO: replace 100 with area height */
    if ((mm.voy < 0) || (mm.voy >= (100-mm.vh))) {
        mm.voy -= dy;
        dy = 0;
    }

    return (dx | dy);
}


/*******************************************************************************
 * Text Box                                                                  
 ******************************************************************************/


Textbox::Textbox(int _left, int _top, int _w, int _h) {
    left = _left;
    top = _top;
    width = _w;
    height = _h;
    fg = 0xF0F0DC;
    bg = 0x00000A;
}

Textbox::~Textbox() {
}

void Textbox::puts(const char *txt) {
    puts_xy(txt, cursor_x, cursor_y);
}

void Textbox::puts_xy(const char *_txt, int x, int y) {
    char *txt = (char*)_txt;
    int l = strlen(txt);
    char nl = txt[l-1];

    // strip trailing newline in display string
    if (nl == '\n') {
        txt = strdup(_txt);
        txt[--l] = 0;
    }

    re_puts(left + x, top + y, fg, bg, txt);
        
    cursor_x = x + l; 
    if (nl == '\n') {
        newline();
        free(txt);
    } 
}

void Textbox::newline() {
    // clear to EOL
    int n = width - cursor_x;
    if (n > 0) {
        char tmp[n+1];
        memset(tmp, ' ', n);
        tmp[n] = 0;
        re_puts(left + cursor_x, top + cursor_y, 0x0, bg, tmp);
    }

    cursor_x = 0;
    cursor_y++;
}

void Textbox::goto_xy(int x, int y) {
    cursor_x = x;
    cursor_y = y;
}

void Textbox::scroll(int dy) {
    offset += dy;
    if (offset < 0) offset = 0;
}

void Textbox::draw() {
    int i;
    cursor_x = 0;
    cursor_y = 0;

    for (i = 0; i < height; i++) {
        if ((offset + i) < lines.size() ) {
            puts( lines[offset + i].c_str() );
        } else {
            puts("");
        }

        newline();
    }

}

void Textbox::message(const char *_txt, char is_temp) {
    int i,j;
    char *txt = strdup(_txt);

    // revoke tmeporary lines
    while (lines.size() > solid_lines) {
        lines.pop_back();
    }
    

    // empty message - just redraw and exit  
    if (strlen(txt) == 0) {
        draw();
        return;
    }

    char *saveptr = NULL;
    char *tok = NULL;
    char line[width+1];
    memset(line, 0, sizeof(line));

    for (tok = strtok_r(txt, " ", &saveptr); 
         tok != NULL; 
         tok = strtok_r(NULL, " ", &saveptr)) {
        
        if ((strlen(line) + strlen(tok)) < width) {
            // add the word to current line
            strcat(line, tok);
            strcat(line, " ");
        } else {
            // the word causes line overflow
            if (strlen(line) > 0) {
                // flush the words added so far
                lines.push_back(line);
                if (!is_temp) solid_lines++;

                memset(line, 0, sizeof(line));
                strcat(line, tok);
                strcat(line, " ");
            } else {
                // line empty - a single word causes line overflow
                // fallback to word split
                j = 0; // index within line
                for (i = 0; i < strlen(tok); i++) {
                    if (j >= width) {
                        // add line
                        lines.push_back(line);
                        if (!is_temp) solid_lines++;
                        memset(line, 0, sizeof(line));
                        j = 0; // begin next line
                    }

                    line[j++] = tok[i];
                }
            } // single word overflow
        } // overflows
    } // tokens

    // last line
    if (strlen(line) > 0) {
        // flush the words added so far
        lines.push_back(line);
        if (!is_temp) solid_lines++;
    }

    // auto-scroll
    // self.offset = max(0, len(self.lines) - self.pagesize)
    
    draw();
    free(txt);
}


